/*
 * $LastChangedDate: 2007-06-21 22:52:33 +0200 (Thu, 21 Jun 2007) $
 * $LastChangedRevision: 208 $
 * $LastChangedBy: david $
 */

#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;

#include <vector>

#include "Log.h"
#include "Client.h"
#include "Server.h"
#include "ClientSocket.h"
#include "BlockingObject.h"

vector<Client*> readyClients;

BlockingObject clients_executing;

/* true cuando hay que finalizar el programa */
bool end = false;


/* Añade un nuevo cliente a la lista de clientes listos para ser ejecutados */
static void addClient(string path, string mode)
{
	Client *newClient;
	newClient = new Client(path, mode);
	readyClients.push_back(newClient);
}

/* Crea un nuevo cliente según los parámetros introducidos desde el teclado */
static void createClient()
{
	string path, mode;

	cout << "Fichero para el cual el cliente debe hacer una petición:\n> ";
	cin >> path;
	do {
		cout << "Tipo de petición 'r'/'rw':\n> ";
		cin >> mode;
	} while (mode != "r" && mode != "rw");
	addClient(path, mode);
}

/* Pide al usuario el path hacia un fichero de configuración y crea una serie
 * de clientes tal como se indica en dicho fichero. El formato del fichero de
 * configuración es el siguiente: cada línea contiene el path hacia el fichero
 * que se debe abrir, un espacio en blanco y el modo en que se debe abrir el
 * fichero. */
static void createClientsFromFile()
{
	string config; /* Path del fichero de configuración */
	string path; /* Path leído del fichero de configuración */
	string mode; /* Modo leído del fichero de configuración */
	ifstream fin;

	cout << "Fichero de configuración:\n";
	cin >> config;

	fin.open(config.c_str());
	if (!fin.is_open()) {
		cerr << "No se ha podido abrir el fichero " << config << endl;
		fin.clear();
		return;
	}

	string line;
	while (getline(fin, line)) {
		stringstream ss(line);
		ss >> path;
		ss >> mode;
		addClient(path, mode);
	}

	fin.close();
}

/* Imprime el menú con las opciones introducibles desde el teclado */
static void printMenu()
{
	cout << endl;
	cout << "c: crear cliente" << endl;
	cout << "f: crear clientes a partir de un fichero de configuración" << endl;
	cout << "s: start clientes" << endl;
	cout << "l: listar clientes listos para ejecutar" << endl;
	cout << "v: fijar el nivel de log" << endl;
	cout << "q: quit" << endl;
	cout << "> ";
}

/* Lista los clientes listos para ser ejecutados */
static void listClients()
{
	vector<Client*>::iterator pd;

	for (pd = readyClients.begin(); pd != readyClients.end(); pd += 1) {
		cout << **pd << endl;
	}
}

/* Empieza a ejecutar los clientes creados */
static void startClients()
{
	vector<Client*>::iterator pd;

	for (pd = readyClients.begin(); pd != readyClients.end(); pd += 1) {
		(*pd)->start();
	}
	readyClients.clear();

	/* TODO ¿Hace falta invocar el delete de los clientes que han terminado
	 * de ejecutarse? */
}

static void setLogLevel()
{
	bool back = false;
	int lvl = 0;
	do {
		cout << "1: Toggle INIT log";
		if (Log::isEnabled(Log::INIT)) {
			cout << " (Habilitado)" << endl;
		} else {
			cout << " (Deshabilitado)" << endl;
		}
		cout << "2: Toggle CONNECTION log";
		if (Log::isEnabled(Log::CONNECTION)) {
			cout << " (Habilitado)" << endl;
		} else {
			cout << " (Deshabilitado)" << endl;
		}

		cout << "3: Toggle IO log";
		if (Log::isEnabled(Log::IO)) {
			cout << " (Habilitado)" << endl;
		} else {
			cout << " (Deshabilitado)" << endl;
		}

		cout << "4: Toggle LOCK log";
		if (Log::isEnabled(Log::LOCK)) {
			cout << " (Habilitado)" << endl;
		} else {
			cout << " (Deshabilitado)" << endl;
		}

		cout << "q: volver al menú principal" << endl;
		cout << "> ";
		char input;
		cin >> input;

		switch (input) {
		case '1':
			lvl = Log::INIT;
			break;
		case '2':
			lvl = Log::CONNECTION;
			break;
		case '3':
			lvl = Log::IO;
			break;
		case '4':
			lvl = Log::LOCK;
			break;
		case 'q':
			back = true;
			break;
		default:
			lvl = 0;
			break;
		}
		if (!back && (lvl != 0)) {
			Log::loglevel ^= lvl;
		}
	} while (!back);
	printMenu();
}


int main (int argc, char const* argv[])
{
	Server * myServer = new Server();

	myServer->start();

	/* Variable para leer del teclado */
	string input;

	do {
		printMenu();
		/* Leer del teclado */

		cin >> input;

		if (input.size() != 1) {
			cout << "Opción no reconocida.\n";
			continue;
		}

		switch (input[0]) {
		case 'c':
			createClient();
			break;
		case 'f':
			createClientsFromFile();
			break;
		case 's':
			startClients();
			break;
		case 'l':
			listClients();
			break;
		case 'q':
			end = true;
			break;
		case 'v':
			setLogLevel();
			break;
		default:
			printMenu();
			break;
		}
	} while (!end);

	bool waited = false;
	clients_executing.synchronize_start();
	if (Client::numExecuting > 0) {
		clients_executing.synchronize_end();
		/* Hay clientes aún ejecutando, esperamos a que acaben */
		clients_executing.wait();
		waited = true;
	}
	if (!waited) {
		clients_executing.synchronize_end();
	}

	cout << "Enviando mensaje de finalización al servidor\n";
	ClientSocket socket("localhost", 4242);
	socket << "KILL";
	std::string ack;
	socket >> ack;

	return 0;
}

